#//------------------------------------------------------------------------------
#//   Copyright 2007-2011 Mentor Graphics Corporation
#//   Copyright 2007-2010 Cadence Design Systems, Inc.
#//   Copyright 2010 Synopsys, Inc.
#//   Copyright 2019 Tuomas Poikela (tpoikela)
#//   All Rights Reserved Worldwide
#//
#//   Licensed under the Apache License, Version 2.0 (the
#//   "License"); you may not use this file except in
#//   compliance with the License.  You may obtain a copy of
#//   the License at
#//
#//       http://www.apache.org/licenses/LICENSE-2.0
#//
#//   Unless required by applicable law or agreed to in
#//   writing, software distributed under the License is
#//   distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
#//   CONDITIONS OF ANY KIND, either express or implied.  See
#//   the License for the specific language governing
#//   permissions and limitations under the License.
#//------------------------------------------------------------------------------

import cocotb
from .uvm_message_defines import uvm_warning

#// Title: Sequence-Related Macros

#//-----------------------------------------------------------------------------
#//
#// Group: Sequence Action Macros
#//
#// These macros are used to start sequences and sequence items on the default
#// sequencer, ~m_sequencer~. This is determined a number of ways.
#// - the sequencer handle provided in the <uvm_sequence_base::start> method
#// - the sequencer used by the parent sequence
#// - the sequencer that was set using the <uvm_sequence_item::set_sequencer> method
#//-----------------------------------------------------------------------------

#// MACRO: `uvm_create
#//
#//| `uvm_create(SEQ_OR_ITEM)
#//
#// This action creates the item or sequence using the factory.  It intentionally
#// does zero processing.  After this action completes, the user can manually set
#// values, manipulate rand_mode and constraint_mode, etc.

#`define uvm_create(SEQ_OR_ITEM) \
#  `uvm_create_on(T, SEQ_OR_ITEM, m_sequencer)
def uvm_create(seq_obj, SEQ_OR_ITEM, m_sequencer):
    return uvm_create_on(seq_obj, SEQ_OR_ITEM, m_sequencer)


#// MACRO: `uvm_do
#//
#//| `uvm_do(SEQ_OR_ITEM)
#//
#// This macro takes as an argument a uvm_sequence_item variable or object.
#// The argument is created using <`uvm_create> if necessary,
#// then randomized.
#// In the case of an item, it is randomized after the call to
#// <uvm_sequence_base::start_item()> returns.
#// This is called late-randomization.
#// In the case of a sequence, the sub-sequence is started using
#// <uvm_sequence_base::start()> with ~call_pre_post~ set to 0.
#// In the case of an item,
#// the item is sent to the driver through the associated sequencer.
#//
#// For a sequence item, the following are called, in order
#//
#//|
#//|   `uvm_create(item)
#//|   sequencer.wait_for_grant(prior) (task)
#//|   this.pre_do(1)                  (task)
#//|   item.randomize()
#//|   this.mid_do(item)               (func)
#//|   sequencer.send_request(item)    (func)
#//|   sequencer.wait_for_item_done()  (task)
#//|   this.post_do(item)              (func)
#//|
#//
#// For a sequence, the following are called, in order
#//
#//|
#//|   `uvm_create(sub_seq)
#//|   sub_seq.randomize()
#//|   sub_seq.pre_start()         (task)
#//|   this.pre_do(0)              (task)
#//|   this.mid_do(sub_seq)        (func)
#//|   sub_seq.body()              (task)
#//|   this.post_do(sub_seq)       (func)
#//|   sub_seq.post_start()        (task)
#//|
#
#`define uvm_do(seq_obj, SEQ_OR_ITEM) \
#  `uvm_do_on_pri_with(seq_obj, SEQ_OR_ITEM, m_sequencer, -1, {})


async def uvm_do(seq_obj, SEQ_OR_ITEM):
    await uvm_do_on_pri_with(seq_obj, SEQ_OR_ITEM, seq_obj.m_sequencer, -1, [])
#
#
#// MACRO: `uvm_do_pri
#//
#//| `uvm_do_pri(SEQ_OR_ITEM, PRIORITY)
#//
#// This is the same as `uvm_do except that the sequence item or sequence is
#// executed with the priority specified in the argument
#
#`define uvm_do_pri(SEQ_OR_ITEM, PRIORITY) \
#  `uvm_do_on_pri_with(SEQ_OR_ITEM, m_sequencer, PRIORITY, {})


#// MACRO: `uvm_do_with
#//
#//| `uvm_do_with(SEQ_OR_ITEM, CONSTRAINTS)
#//
#// This is the same as `uvm_do except that the constraint block in the 2nd
#// argument is applied to the item or sequence in a randomize with statement
#// before execution.
#
#`define uvm_do_with(SEQ_OR_ITEM, CONSTRAINTS) \
#  `uvm_do_on_pri_with(SEQ_OR_ITEM, m_sequencer, -1, CONSTRAINTS)

async def uvm_do_with(seq_obj, SEQ_OR_ITEM, *CONSTRAINTS):
    await uvm_do_on_pri_with(seq_obj, SEQ_OR_ITEM, seq_obj.m_sequencer, -1,
            *CONSTRAINTS)


#// MACRO: `uvm_do_pri_with
#//
#//| `uvm_do_pri_with(SEQ_OR_ITEM, PRIORITY, CONSTRAINTS)
#//
#// This is the same as `uvm_do_pri except that the given constraint block is
#// applied to the item or sequence in a randomize with statement before
#// execution.
#
#`define uvm_do_pri_with(SEQ_OR_ITEM, PRIORITY, CONSTRAINTS) \
#  `uvm_do_on_pri_with(SEQ_OR_ITEM, m_sequencer, PRIORITY, CONSTRAINTS)
#
#
#//-----------------------------------------------------------------------------
#//
#// Group: Sequence on Sequencer Action Macros
#//
#// These macros are used to start sequences and sequence items on a specific
#// sequencer. The sequence or item is created and executed on the given
#// sequencer.
#//-----------------------------------------------------------------------------
#
#// MACRO: `uvm_create_on
#//
#//| `uvm_create_on(SEQ_OR_ITEM, SEQR)
#//
#// This is the same as <`uvm_create> except that it also sets the parent sequence
#// to the sequence in which the macro is invoked, and it sets the sequencer to
#// the specified ~SEQR~ argument.

#`define uvm_create_on(SEQ_OR_ITEM, SEQR) \
#  begin \
#  uvm_object_wrapper w_; \
#  w_ = SEQ_OR_ITEM.get_type(); \
#  $cast(SEQ_OR_ITEM , create_item(w_, SEQR, `"SEQ_OR_ITEM`"));\
#  end
def uvm_create_on(seq_obj, SEQ_OR_ITEM, SEQR):
    w_ = SEQ_OR_ITEM.get_type()
    return seq_obj.create_item(w_, SEQR, "SEQ_OR_ITEM")


#// MACRO: `uvm_do_on
#//
#//| `uvm_do_on(SEQ_OR_ITEM, SEQR)
#//
#// This is the same as <`uvm_do> except that it also sets the parent sequence to
#// the sequence in which the macro is invoked, and it sets the sequencer to the
#// specified ~SEQR~ argument.
#
#`define uvm_do_on(SEQ_OR_ITEM, SEQR) \
#  `uvm_do_on_pri_with(SEQ_OR_ITEM, SEQR, -1, {})
#
#
#// MACRO: `uvm_do_on_pri
#//
#//| `uvm_do_on_pri(SEQ_OR_ITEM, SEQR, PRIORITY)
#//
#// This is the same as <`uvm_do_pri> except that it also sets the parent sequence
#// to the sequence in which the macro is invoked, and it sets the sequencer to
#// the specified ~SEQR~ argument.
#
#`define uvm_do_on_pri(SEQ_OR_ITEM, SEQR, PRIORITY) \
#  `uvm_do_on_pri_with(SEQ_OR_ITEM, SEQR, PRIORITY, {})
#
#
#// MACRO: `uvm_do_on_with
#//
#//| `uvm_do_on_with(SEQ_OR_ITEM, SEQR, CONSTRAINTS)
#//
#// This is the same as <`uvm_do_with> except that it also sets the parent
#// sequence to the sequence in which the macro is invoked, and it sets the
#// sequencer to the specified ~SEQR~ argument.
#// The user must supply brackets around the constraints.
#
#`define uvm_do_on_with(SEQ_OR_ITEM, SEQR, CONSTRAINTS) \
#  `uvm_do_on_pri_with(SEQ_OR_ITEM, SEQR, -1, CONSTRAINTS)

#// MACRO: `uvm_do_on_pri_with
#//
#//| `uvm_do_on_pri_with(SEQ_OR_ITEM, SEQR, PRIORITY, CONSTRAINTS)
#//
#// This is the same as `uvm_do_pri_with except that it also sets the parent
#// sequence to the sequence in which the macro is invoked, and it sets the
#// sequencer to the specified ~SEQR~ argument.
#
#`define uvm_do_on_pri_with(SEQ_OR_ITEM, SEQR, PRIORITY, CONSTRAINTS) \
#  begin \
#  uvm_sequence_base __seq; \
#  `uvm_create_on(SEQ_OR_ITEM, SEQR) \
#  if (!$cast(__seq,SEQ_OR_ITEM)) start_item(SEQ_OR_ITEM, PRIORITY);\
#  if ((__seq == null || !__seq.do_not_randomize) && !SEQ_OR_ITEM.randomize() with CONSTRAINTS ) begin \
#    `uvm_warning("RNDFLD", "Randomization failed in uvm_do_with action") \
#  end\
#  if (!$cast(__seq,SEQ_OR_ITEM)) finish_item(SEQ_OR_ITEM, PRIORITY); \
#  else __seq.start(SEQR, this, PRIORITY, 0); \
#  end

async def uvm_do_on_pri_with(seq_obj, SEQ_OR_ITEM, SEQR, PRIORITY, *CONSTRAINTS):
    """
    This is the same as uvm_do_pri_with except that it also sets the parent
    sequence to the sequence in which the function is invoked, and it sets the
    sequencer to the specified ~SEQR~ argument.

    Args:
        seq_obj (UVMSequence): Seq to start.
        SEQ_OR_ITEM (UVMSequence|UVMSequenceItem): 
        SEQR (UVMSequencer): Runs sequence on this sequencer.
        CONSTRAINTS (constraints): Randomization constraints
    """
    from ..seq.uvm_sequence import UVMSequence
    _seq = uvm_create_on(seq_obj, SEQ_OR_ITEM, SEQR)
    if isinstance(_seq, UVMSequence):
        await SEQ_OR_ITEM.start(SEQR, seq_obj, PRIORITY, 0)
    else:
        # TODO handle constraints
        await seq_obj.start_item(SEQ_OR_ITEM, PRIORITY)
        if SEQ_OR_ITEM.randomize_with(*CONSTRAINTS) is False:
            uvm_warning("RNDFLD", "Randomization failed in uvm_do_with action")
        await seq_obj.finish_item(SEQ_OR_ITEM, PRIORITY)


#//-----------------------------------------------------------------------------
#//
#// Group: Sequence Action Macros for Pre-Existing Sequences
#//
#// These macros are used to start sequences and sequence items that do not
#// need to be created.
#//-----------------------------------------------------------------------------


#// MACRO: `uvm_send
#//
#//| `uvm_send(SEQ_OR_ITEM)
#//
#// This macro processes the item or sequence that has been created using
#// `uvm_create.  The processing is done without randomization.  Essentially, an
#// `uvm_do without the create or randomization.

#`define uvm_send(SEQ_OR_ITEM) \
#  `uvm_send_pri(SEQ_OR_ITEM, -1)


#// MACRO: `uvm_send_pri
#//
#//| `uvm_send_pri(SEQ_OR_ITEM, PRIORITY)
#//
#// This is the same as `uvm_send except that the sequence item or sequence is
#// executed with the priority specified in the argument.
#
#`define uvm_send_pri(SEQ_OR_ITEM, PRIORITY) \
#  begin \
#  uvm_sequence_base __seq; \
#  if (!$cast(__seq,SEQ_OR_ITEM)) begin \
#     start_item(SEQ_OR_ITEM, PRIORITY);\
#     finish_item(SEQ_OR_ITEM, PRIORITY);\
#  end \
#  else __seq.start(__seq.get_sequencer(), this, PRIORITY, 0);\
#  end


#// MACRO: `uvm_rand_send
#//
#//| `uvm_rand_send(SEQ_OR_ITEM)
#//
#// This macro processes the item or sequence that has been already been
#// allocated (possibly with `uvm_create). The processing is done with
#// randomization.  Essentially, an `uvm_do without the create.
#
#`define uvm_rand_send(SEQ_OR_ITEM) \
#  `uvm_rand_send_pri_with(SEQ_OR_ITEM, -1, {})
#
#
#// MACRO: `uvm_rand_send_pri
#//
#//| `uvm_rand_send_pri(SEQ_OR_ITEM, PRIORITY)
#//
#// This is the same as `uvm_rand_send except that the sequence item or sequence
#// is executed with the priority specified in the argument.
#
#`define uvm_rand_send_pri(SEQ_OR_ITEM, PRIORITY) \
#  `uvm_rand_send_pri_with(SEQ_OR_ITEM, PRIORITY, {})
#
#
#// MACRO: `uvm_rand_send_with
#//
#//| `uvm_rand_send_with(SEQ_OR_ITEM, CONSTRAINTS)
#//
#// This is the same as `uvm_rand_send except that the given constraint block is
#// applied to the item or sequence in a randomize with statement before
#// execution.
#
#`define uvm_rand_send_with(SEQ_OR_ITEM, CONSTRAINTS) \
#  `uvm_rand_send_pri_with(SEQ_OR_ITEM, -1, CONSTRAINTS)
#
#
#// MACRO: `uvm_rand_send_pri_with
#//
#//| `uvm_rand_send_pri_with(SEQ_OR_ITEM, PRIORITY, CONSTRAINTS)
#//
#// This is the same as `uvm_rand_send_pri except that the given constraint block
#// is applied to the item or sequence in a randomize with statement before
#// execution.
#
#`define uvm_rand_send_pri_with(SEQ_OR_ITEM, PRIORITY, CONSTRAINTS) \
#  begin \
#  uvm_sequence_base __seq; \
#  if (!$cast(__seq,SEQ_OR_ITEM)) start_item(SEQ_OR_ITEM, PRIORITY);\
#  else __seq.set_item_context(this,SEQ_OR_ITEM.get_sequencer()); \
#  if ((__seq == null || !__seq.do_not_randomize) && !SEQ_OR_ITEM.randomize() with CONSTRAINTS ) begin \
#    `uvm_warning("RNDFLD", "Randomization failed in uvm_rand_send_with action") \
#  end\
#  if (!$cast(__seq,SEQ_OR_ITEM)) finish_item(SEQ_OR_ITEM, PRIORITY);\
#  else __seq.start(__seq.get_sequencer(), this, PRIORITY, 0);\
#  end
#
#
#`define uvm_create_seq(UVM_SEQ, SEQR_CONS_IF) \
#  `uvm_create_on(UVM_SEQ, SEQR_CONS_IF.consumer_seqr) \
#
#`define uvm_do_seq(UVM_SEQ, SEQR_CONS_IF) \
#  `uvm_do_on(UVM_SEQ, SEQR_CONS_IF.consumer_seqr) \
#
#`define uvm_do_seq_with(UVM_SEQ, SEQR_CONS_IF, CONSTRAINTS) \
#  `uvm_do_on_with(UVM_SEQ, SEQR_CONS_IF.consumer_seqr, CONSTRAINTS) \
#
#
#
#//-----------------------------------------------------------------------------
#//
#// Group- Sequence Library
#//
#//-----------------------------------------------------------------------------


#// MACRO: `uvm_add_to_sequence_library
#//
#// Adds the given sequence ~TYPE~ to the given sequence library ~LIBTYPE~
#//
#//| `uvm_add_to_seq_lib(TYPE,LIBTYPE)
#//
#// Invoke any number of times within a sequence declaration to statically add
#// that sequence to one or more sequence library types. The sequence will then
#// be available for selection and execution in all instances of the given
#// sequencer types.
#//
#//| class seqA extends uvm_sequence_base #(simple_item);
#//|
#//|    function new(string name=`"TYPE`");
#//|      super.new(name);
#//|    endfunction
#//|
#//|    `uvm_object_utils(seqA)
#//|
#//|    `uvm_add_to_seq_lib(seqA, simple_seq_lib_RST)
#//|    `uvm_add_to_seq_lib(seqA, simple_seq_lib_CFG)
#//|
#//|    virtual task body(); \
#//|      `uvm_info("SEQ_START", {"Executing sequence '", get_full_name(),
#//|                             "' (",get_type_name(),")"},UVM_HIGH)
#//|      #10;
#//|    endtask
#//|
#//|  endclass
#
#
#`define uvm_add_to_seq_lib(TYPE,LIBTYPE) \
#   static bit add_``TYPE``_to_seq_lib_``LIBTYPE =\
#      LIBTYPE::m_add_typewide_sequence(TYPE::get_type());
#
#
#
#// MACRO: `uvm_sequence_library_utils
#//
#//| `uvm_sequence_library_utils(TYPE)
#//
#// Declares the infrastructure needed to define extensions to the
#// <uvm_sequence_library> class. You define new sequence library subtypes
#// to statically specify sequence membership from within sequence
#// definitions. See also <`uvm_add_to_sequence_library> for more information.
#//
#//
#//| typedef simple_seq_lib uvm_sequence_library #(simple_item);
#//|
#//| class simple_seq_lib_RST extends simple_seq_lib;
#//|
#//|   `uvm_object_utils(simple_seq_lib_RST)
#//|
#//|   `uvm_sequence_library_utils(simple_seq_lib_RST)
#//|
#//|   function new(string name="");
#//|     super.new(name);
#//|   endfunction
#//|
#//| endclass
#//
#// Each library, itself a sequence, can then be started independently
#// on different sequencers or in different phases of the same sequencer.
#// See <uvm_sequencer_base::start_phase_sequence> for information on
#// starting default sequences.
#
#`define uvm_sequence_library_utils(TYPE) \
#\
#   static protected uvm_object_wrapper m_typewide_sequences[$]; \
#   \
#   function void init_sequence_library(); \
#     foreach (TYPE::m_typewide_sequences[i]) \
#       sequences.push_back(TYPE::m_typewide_sequences[i]); \
#   endfunction \
#   \
#   static function void add_typewide_sequence(uvm_object_wrapper seq_type); \
#     if (m_static_check(seq_type)) \
#       TYPE::m_typewide_sequences.push_back(seq_type); \
#   endfunction \
#   \
#   static function void add_typewide_sequences(uvm_object_wrapper seq_types[$]); \
#     foreach (seq_types[i]) \
#       TYPE::add_typewide_sequence(seq_types[i]); \
#   endfunction \
#   \
#   static function bit m_add_typewide_sequence(uvm_object_wrapper seq_type); \
#     TYPE::add_typewide_sequence(seq_type); \
#     return 1; \
#   endfunction
#
#
#
#//-----------------------------------------------------------------------------
#//
#// Group: Sequencer Subtypes
#//
#//-----------------------------------------------------------------------------
#
#
#// MACRO: `uvm_declare_p_sequencer
#//
#// This macro is used to declare a variable ~p_sequencer~ whose type is
#// specified by ~SEQUENCER~.
#//
#//| `uvm_declare_p_sequencer(SEQUENCER)
#//
#// The example below shows using the `uvm_declare_p_sequencer macro
#// along with the uvm_object_utils macros to set up the sequence but
#// not register the sequence in the sequencer's library.
#//
#//| class mysequence extends uvm_sequence#(mydata);
#//|   `uvm_object_utils(mysequence)
#//|   `uvm_declare_p_sequencer(some_seqr_type)
#//|   task body;
#//|     //Access some variable in the user's custom sequencer
#//|     if(p_sequencer.some_variable) begin
#//|       ...
#//|     end
#//|   endtask
#//| endclass
#//

#`define uvm_declare_p_sequencer(SEQUENCER) \
#  SEQUENCER p_sequencer;\
#  virtual function void m_set_p_sequencer();\
#    super.m_set_p_sequencer(); \
#    if( !$cast(p_sequencer, m_sequencer)) \
#        `uvm_fatal("DCLPSQ", \
#        $sformatf("%m %s Error casting p_sequencer, please verify that this sequence/sequence item is intended to execute on this type of sequencer", get_full_name())) \
#  endfunction
